#include "head.h"
#define LENGTH 4096

int send_file(int socketfd, char *name){
    int fd = open(name, O_RDONLY);
    send(socketfd, &fd, sizeof(fd), 0);
    if(fd == -1){
        printf("没有客户端要求的文件\n");
        return -1;
    }
    
    //发送文件大小
    struct stat stat_t;
    fstat(fd, &stat_t);
    printf("file size: %ld\n", stat_t.st_size);
    send(socketfd, &stat_t.st_size, sizeof(off_t), MSG_WAITALL);
    
    //发送文件
    while(1){
        char buf[1024]={0};
        int ret_read = read(fd, buf, sizeof(buf));
        if(ret_read == 0){
            int flag_exit = 0;
            send(socketfd, &flag_exit, sizeof(int),MSG_NOSIGNAL);
            printf("文件传输完毕\n");
            break;
        }
        send(socketfd, &ret_read, sizeof(int), MSG_NOSIGNAL);
        send(socketfd, buf, ret_read, MSG_NOSIGNAL);
    }
    close(fd);
    return 0;
}

int send_bigfile(int socketfd, char *name){
    int fd = open(name, O_RDWR);
    send(socketfd, &fd, sizeof(int), 0);
    if(fd == -1){
        printf("没有客户端要求的文件\n");
        return -1;    
    }

    //获取客户端要求下载文件的大小
    struct stat stat_file;
    fstat(fd, &stat_file);
    printf("file size: %ld\n", stat_file.st_size);
    
    //接收客户端现有文件的大小
    off_t real_size;
    recv(socketfd, &real_size, sizeof(off_t), MSG_WAITALL);
    printf("real size: %ld\n", real_size);
    
    //判断文件是否一致，一致则直接结束
    int flag_same = 0;
    if(real_size == stat_file.st_size){
        printf("文件完全一致，不需要上传\n");
        flag_same = 1;
        send(socketfd, &flag_same, sizeof(int), MSG_NOSIGNAL);
        return 0;
    }
    send(socketfd, &flag_same, sizeof(int), MSG_NOSIGNAL);

    //position用于记录当前文件指针的位置
    //因为position要作为mmap的offset参数，所以只能是LENGTH的整数倍
    int position = (real_size/LENGTH)*LENGTH;
    printf("position: %d\n", position);
    
    //每次传输的长度
    int length = LENGTH;
    //发送文件
    while(1){
        //如果总文件的大小和当前传输到的位置(positionon)相差的值小于一次传输的长度，就把长度改为这个差值
        if(stat_file.st_size - position < LENGTH){
            length = stat_file.st_size -position;
        }
        //从position的位置映射length的长度到file_read
        char *file_read = (char *)mmap(NULL, length, PROT_READ|PROT_WRITE, MAP_SHARED, fd, position);
        ERROR_CHECK(file_read, MAP_FAILED, "mmap");
        //传输内容的长度
        send(socketfd, &length, sizeof(int), MSG_NOSIGNAL);
        printf("length = %d\n", length);
        //传输的内容
        send(socketfd, file_read, length, MSG_NOSIGNAL);
        munmap(file_read, length);
        //传输一次后，更新position的值
        position += LENGTH;
        printf("position: %d\n", position);
        //如果position的值大于等于文件的总长度，说明position已经走到了文件的末尾
        //向服务端发送一个长度，值为0，表示传输结束
        if(position >= stat_file.st_size){
            length = 0;
            send(socketfd, &length, sizeof(int), MSG_NOSIGNAL);
            printf("文件传输完毕\n");
            break;
        }
    }
    close(fd);
    return 0;
}


int recv_file(int socketfd, char *name){
    chdir("download");
    int fd = open(name, O_WRONLY|O_CREAT|O_TRUNC, 0664);
    chdir("..");
    //为要接收的文件预留空间
    off_t file_size;
    recv(socketfd, &file_size, sizeof(off_t),MSG_WAITALL);
    int ret_fruncate = ftruncate(fd, file_size);
    ERROR_CHECK(ret_fruncate, -1, "ftruncate");

    //接收文件
    while(1){
        int len;
        char buf[1024]={0};
        recv(socketfd, &len, sizeof(len), MSG_WAITALL);
        if(len == 0){
            printf("文件接收完毕\n");
            break;
        }
        recv(socketfd, buf, len, MSG_WAITALL);
        write(fd, buf, len);
    }
    close(fd);
    return 0;
}

int recv_bigfile(int socketfd, char *name){
    chdir("download");
    int fd = open(name, O_WRONLY|O_CREAT, 0664);
    chdir("..");
    //现有文件大小
    struct stat stat_file;
    fstat(fd, &stat_file);
    printf("file size: %ld\n", stat_file.st_size);
    //将现有文件大小发给客户端
    send(socketfd, &stat_file.st_size, sizeof(off_t), MSG_NOSIGNAL);
    
    int flag_same;
    recv(socketfd, &flag_same, sizeof(int), MSG_WAITALL);
    if(flag_same == 1){
        printf("文件完全一致，不需要上传\n");
        return 0;
    }

    //获取断点
    off_t position = (stat_file.st_size/LENGTH)*LENGTH;
    printf("position: %ld\n", position);
    //改变文件指针的指向到断点
    int ret_lseek = lseek(fd, position, SEEK_SET);
    ERROR_CHECK(ret_lseek, -1, "lseek");
    //接收文件
    while(1){
        int buf_len;
        char buf[5000] = {0};
        recv(socketfd, &buf_len, sizeof(int), MSG_WAITALL);
        printf("buf_len: %d\n", buf_len);
        if(buf_len == 0){
            printf("文件下载完毕\n");
            break;
        }
        recv(socketfd, buf, buf_len, MSG_WAITALL);
        printf("buf: %s\n", buf);
        write(fd, buf, buf_len);
    }
    close(fd);
    return 0;

}
